package impls.agents;

import impls.classes.Constants;
import interfaces.agents.Station;
import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.AMSService;
import jade.domain.FIPAAgentManagement.AMSAgentDescription;
import jade.domain.FIPAAgentManagement.SearchConstraints;
import jade.lang.acl.ACLMessage;

import java.util.Random;

/**
 * agent representing a station
 * 
 * @author thailow
 * 
 */
public class StationAgent extends ActorAgent implements Station {

	private static final long serialVersionUID = -1851946521706303754L;

	private static final String TYPE = Constants.STATION;

	private float withoutVisits = 1;

	private float price;

	private String fuelCompany;

	/**
	 * setup method of StationAgent
	 */
	public void setup() {
		super.setup();
		super.TYPE = TYPE;

		Object[] args = getArguments();

		// starting station at random position with chosen/random price and
		// fuelCompany
		if (args != null && args.length > 0) {
			Integer i = Integer.valueOf(args[0].toString());
			price = i.intValue();
			if (args.length > 1) {
				fuelCompany = args[1].toString();
			}
		} else {
			Random r = new Random();
			int n = r.nextInt(Constants.FUELCOMPANIES.length);
			fuelCompany = Constants.FUELCOMPANIES[n];
			price = 0;
			while (price < Constants.MINPRICE) {
				price = r.nextInt(Math.round(Constants.MAXPRICE));
			}
		}

		sendMessage(toString() + "Created at (" + pos.getX() + ","
				+ pos.getY() + "). Company: " + fuelCompany + ", Price: "
				+ price);

		AMSAgentDescription[] agents = null;
		try {
			SearchConstraints c = new SearchConstraints();
			c.setMaxResults(new Long(-1));
			agents = AMSService.search(this, new AMSAgentDescription(), c);
		} catch (Exception e) {
			System.out.println(toString() + "Problem searching AMS: " + e);
			e.printStackTrace();
		}

		// send initial price to all agents
		ACLMessage initPrice = new ACLMessage(ACLMessage.INFORM);
		initPrice.setConversationId(Constants.MYFUELPRICE);
		initPrice.setContent(price + "," + pos.getX() + "," + pos.getY() + ","
				+ price);
		// sending to all agents
		for (int i = 0; i < agents.length; i++) {
			if (agents[i].getName() != getAID()) {
				initPrice.addReceiver(agents[i].getName());
			}
		}
		initPrice.setSender(getAID());
		send(initPrice);

		// add behaviour for price calculation and graphics update
		addBehaviour(new TickerBehaviour(this, Constants.INTERVAL) {
			private static final long serialVersionUID = 7255153644865671182L;

			// adopt price and send position to graphics agent
			protected void onTick() {
				adoptPrice();
				String[] include = {Float.toString(price)};
				sendPos(include);
			}
		});

		// add behaviour for message handling
		addBehaviour(new CyclicBehaviour(this) {
			private static final long serialVersionUID = -3795810583071798926L;

			public void action() {
				ACLMessage msg = receive();
				// receiving messages from cars
				if (msg != null) {
					if (msg.getConversationId().equals(Constants.FUELPRICE)) {
						// calculate price including reductions
						float redPrice = calculatePrice(msg.getContent());
						ACLMessage answer = new ACLMessage(ACLMessage.INFORM);
						answer.setConversationId(Constants.MYFUELPRICE);
						answer.setContent(fuelCompany + "," + pos.getX() + ","
								+ pos.getY() + "," + redPrice);
						answer.addReceiver(msg.getSender());
						answer.setSender(getAID());
						send(answer);
					} else if (msg.getConversationId().equals(
							Constants.REFUELED)) {
						// recognize that a car was refueled
						refueled(Integer.valueOf(msg.getContent()));
					} else if (msg.getConversationId().equals(
							Constants.GRAPHICSAID)) {
						// receiving initial message from graphics agent
						AID aid = new AID(msg.getContent(), false);
						graphicsAID = aid;
					}
				}
			}
		});
	}

	/**
	 * recognize that a car was refueled and reset withoutVisits
	 * 
	 * @param liters
	 */
	public void refueled(int liters) {
		sendMessage(toString() + "Thanks! You got " + liters
				+ " liters.");
		withoutVisits = 1;
	}

	/**
	 * adopt price to current situation (increase or decrease) following a
	 * hyperbola
	 */
	public void adoptPrice() {
		withoutVisits += 0.001;
		float temp = price / withoutVisits;
		price = Constants.MINPRICE + temp;
		price = price > Constants.MAXPRICE ? Constants.MAXPRICE : price;
	}

	/**
	 * calculate price including reductions
	 * 
	 * @param content
	 * @return
	 */
	public float calculatePrice(String content) {
		float redPrice = price;
		String[] temp = content.split(",");
		String lastFuelCompany = temp[0];
		temp = temp[1].split(";");
		// reduction 1:
		// reduce price if car refueled at this station's company in last
		// TIMEDIF mili seconds
		for (int i = 0; i < temp.length; i++) {
			String[] values = temp[i].split(":");
			if (values[0].equals(fuelCompany)) {
				if (Long.valueOf(values[1]) > System.currentTimeMillis()
						- Constants.TIMEDIF) {
					redPrice -= redPrice * Constants.DISCOUNT / 100;
				}
			}
		}
		// reduction 2:
		// reduce price if the car's last refuel was at this station's company
		if (lastFuelCompany.equals(fuelCompany)) {
			redPrice -= redPrice * Constants.DISCOUNT / 100;
		}
		sendMessage(toString() + "Real Price: " + price
				+ ", Your Price " + redPrice);
		return redPrice;
	}
}
